<?php

namespace Gaolei\WechatPay;

use Gaolei\WechatPay\lib\WechatPay;

/**
 * Class JsPay 微信内浏览器发起支付
 * @package common\services\payment\wechat
 * @author: gaolei 2021/4/23 2:58 下午
 * @protected string $appKey 公众号AppSecret
 */
class JsPay extends WechatPay
{

    protected $appKey;//微信支付申请对应的公众号的APP Key

    /**
     * 创建实例
     * @param array $payConfig
     * @return JsPay
     * @author: gaolei 2021/4/27 3:32 下午
     */
    public static function create(array $payConfig)
    {
        $instance = parent::create($payConfig);
        $instance->appKey = @$payConfig['appKey'];
        return $instance;
    }

    /**
     * 预生成 订单创建数据包
     * @param array $params
     * @return array
     * @throws \Exception
     * @author: gaolei 2021/4/27 3:33 下午
     */
    public function makeOrderParams(array $params): array
    {
        if (!isset($params['open'])) {
            throw new \Exception('params 中缺少 open 参数');
        }
        return [
            'openid' => $params['open'],
            'spbill_create_ip' => '127.0.0.1',
            'trade_type' => 'JSAPI',
        ];
    }

    /**
     * 后处理 wechat订单数据包
     * @param $packageXml
     * @return array|mixed
     * @throws \Exception
     * @author: gaolei 2021/4/24 12:06 下午
     */
    public function afterTreatment($packageXml): array
    {
        if ($packageXml === false) {
            return $this->failure('订单创建失败', ['error' => 'parse xml error']);
        }
        $package = $this->xmlToArray($packageXml);
        if ($package['return_code'] === 'SUCCESS' && $package['result_code'] === 'SUCCESS') {
            $arr = [
                "appId" => $this->appId,
                "timeStamp" => (string)time(),
                "nonceStr" => $this->createNonceStr(),
                "package" => "prepay_id=" . $package['prepay_id'],
                "signType" => 'MD5',
            ];
            $arr['paySign'] = $this->getSign($arr, $this->apiKey);
            return $this->success('订单创建成功', $arr);
        }
        return $this->failure('订单创建失败', $package);
    }

    /**
     * 通过跳转获取用户的openid，跳转流程如下：
     * 1、设置自己需要调回的url及其其他参数，跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
     * 2、微信服务处理完成之后会跳转回用户redirect_uri地址，此时会带上一些参数，如：code
     * @param string $code
     * @return array 获取结果
     */
    public function getOpenid(string $code = ''): array
    {
        try {
            if (empty($code)) {
                if (!isset($_GET['code'])) {
                    return $this->failure('openID 获取失败');
                }
                $code = $_GET['code'];
            }
            $openid = $this->getOpenidFromMp($code);
            return $this->success('获取成功', $openid);
        } catch (\Throwable $t) {
            return $this->failure($t->getMessage());
        }
    }

    /**
     * 通过code从工作平台获取openid机器access_token
     * @param string $code 微信跳转回来带上的code
     * @return string openid
     * @throws \Exception
     */
    public function getOpenidFromMp(string $code): string
    {
        // 获取请求连接
        $url = $this->_createOauthUrlForOpenid($code);
        // 执行请求
        $res = $this->curlGet($url);
        // 取出openid
        $data = json_decode($res, true);
        if (!isset($data['openid'])) {
            $errMsg = 'CODE：' . @$data['errcode'] . '，ERROR：' . @$data['errmsg'];
            throw new \Exception($errMsg);
        }
        return $data['openid'];
    }

    /**
     * 构造获取open和access_toke的url地址
     * @param string $code 微信跳转带回的code
     * @return string 请求的url
     */
    private function _createOauthUrlForOpenid(string $code): string
    {
        $urlObj["appid"] = $this->appId;
        $urlObj["secret"] = $this->appKey;
        $urlObj["code"] = $code;
        $urlObj["grant_type"] = "authorization_code";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString;
    }

    /**
     * 构造获取code的url连接
     * @param string $redirectUrl 微信服务器回跳的url，需要url编码
     * @param string $state
     * @return string 返回构造好的url
     */
    public function _createOauthUrlForCode(string $redirectUrl, string $state = 'STATE'): string
    {
        $urlObj["appid"] = $this->appId;
        $urlObj["redirect_uri"] = $redirectUrl;
        $urlObj["response_type"] = "code";
        $urlObj["scope"] = "snsapi_base";
        $urlObj["state"] = $state . "#wechat_redirect";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString;
    }

    /**
     * 拼接签名字符串
     * @param array $urlObj
     * @return string 返回已经拼接好的字符串
     */
    private function ToUrlParams(array $urlObj): string
    {
        if (isset($urlObj['sign'])) {
            unset($urlObj['sign']);
        }
        $lines = [];
        foreach ($urlObj as $k => $v) {
            $lines[] = "$k=$v";
        }
        return implode('&', $lines);
    }
}